import { CCCAgentsError } from "../CCCAgentsError";
import { CompressionSpec } from "./CompressionSpec";
import { BuiltInSensorType, IBuiltInSensor } from "./IBuiltInSensor";
import { ISensor } from "./ISensor";
import { ObservationSpec } from "./ObservationSpec";
import { ObservationWriter } from "./ObservationWriter";


/**
 * @en
 *  A Sensor that allows to observe a variable number of entities.
 * 
 * @zh
 * 一个传感器，允许观察可变数量的实体。
 */
export class BufferSensor implements ISensor, IBuiltInSensor {
    private _name: string;
    private _maxNumObs: number;
    private _obsSize: number;
    private _observationBuffer: Uint8Array;
    private _currentNumObservables: number;
    private _observationSpec: ObservationSpec;

    constructor(maxNumberObs: number, obsSize: number, name: string) {
        this._name = name;
        this._maxNumObs = maxNumberObs;
        this._obsSize = obsSize;
        this._observationBuffer = new Uint8Array(this._obsSize * this._maxNumObs);
        this._currentNumObservables = 0;
        this._observationSpec = ObservationSpec.variableLength(this._maxNumObs, this._obsSize);
    }

    public getObservationSpec(): ObservationSpec {
        return this._observationSpec;
    }

    /**
     * @en
     * Appends an observation to the buffer. If the buffer is full (maximum number
     * of observation is reached) the observation will be ignored. the length of
     * the provided observation array must be equal to the observation size of
     * the buffer sensor.
     * 
     * @zh
     * 将观察添加到缓冲区。如果缓冲区已满（达到最大观察次数），则将忽略观察。提供的观察数组的长度必须等于缓冲区传感器的观察大小。
     * 
     * 
     * @param obs the float array observation
     * @returns 
     */
    public appendObservation(obs: number[]): void {
        if (obs.length !== this._obsSize) {
            throw new CCCAgentsError(
                `The BufferSensor was expecting an observation of size ${this._obsSize} but received ${obs.length} observations instead.`
            );
        }
        if (this._currentNumObservables >= this._maxNumObs) {
            return;
        }
        for (let i = 0; i < obs.length; i++) {
            this._observationBuffer[this._currentNumObservables * this._obsSize + i] = obs[i];
        }
        this._currentNumObservables++;
    }

    public write(writer: ObservationWriter): number {
        for (let i = 0; i < this._obsSize * this._maxNumObs; i++) {
            writer.setItem(i,this._observationBuffer[i]);
        }
        return this._obsSize * this._maxNumObs;
    }

    public getCompressedObservation(): Uint8Array {
        return new Uint8Array();
    }

    public update(): void {
        this.reset();
    }

    public reset(): void {
        this._currentNumObservables = 0;
        this._observationBuffer.fill(0);
    }

    public getCompressionSpec(): CompressionSpec {
        return CompressionSpec.default();
    }

    public getName(): string {
        return this._name;
    }

    public getBuiltInSensorType(): BuiltInSensorType {
        return BuiltInSensorType.BufferSensor;
    }
}